﻿function MRUList(settings, key) {

    var self = this;

    var items = ko.observableArray([]);

    var firstItem = ko.computed(function () {
        var array = items();
        if (array.length > 0) return array[0];
        return null;
    });

    var max = 10;

    function unify(array) {
        var res = [];
        var map = {};
        for (var i = 0; i < array.length; i++) {
            var arrayItem = array[i];
            if (!map[arrayItem]) {
                map[arrayItem] = true;
                res.push(arrayItem);
                if (res.length >= max) break;
            }
        }
        return res;
    }

    function addItem(item) {
        item = !utils.isNullOrWhiteSpace(item) ? item : '';
        var array = items();
        array.unshift(item);
        array = unify(array);
        items(array);
        store();
    }

    function store() {
        var array = items();
        settings.setObject(key, array);
    }

    items(unify(settings.getObject(key) || []));

    self.addItem = addItem;
    self.firstItem = firstItem;
    self.items = items;
}

function UTF8() {

    var self = this;

    function getBytes(str) {
        var len = str.length;
        var res = new Array(len);
        for (var i = 0; i < len; i++)
            res[i] = str.charCodeAt(i);
        return res;
    }

    function getString(array) {
        var res = '';
        for (var i = 0; i < array.length; i++) {
            var bt = array[i];
            res += String.fromCharCode(bt);
        }
        return res;
    }

    self.getBytes = getBytes;

    self.getString = getString;
}

function HexString() {

    var hex = '0123456789abcdef';

    function format(array) {
        var res = '';
        for (var i = 0; i < array.length; i++) {
            var bt = array[i];
            res += hex.charAt(bt >> 4) + hex.charAt(bt & 0x0f);
        }
        return res;
    }

    function parse(str) {
        var array = [];
        str = str.toLowerCase();
        for (var i = 0; i < str.length / 2; i++) {
            var h = str.charAt(i * 2);
            var l = str.charAt(i * 2 + 1);
            h = hex.indexOf(h);
            l = hex.indexOf(l);
            if (h < 0 || l < 0) return array;
            array.push(h * 16 + l);
        }
        return array;
    }

    this.parse = parse;
    this.format = format;
}

function SpinnerModel() {

    var dispatcher = $({});

    var value = 0;

    var self = this;

    this.loading = function () {
        if (arguments.length == 0) return value;
        var newVal = arguments[0];
        if (value != newVal) {
            value = newVal;
            dispatcher.trigger('changed');
        }
        return value;
    };

    this.enter = function () {
        self.loading(self.loading() + 1);
    };

    this.leave = function () {
        self.loading(self.loading() - 1);
    };

    this.addChanged = function (handler) {
        dispatcher.bind('changed', handler);
    };

}

(function ($) {

    function getWatcher(elem) {
        var $this = $(elem);
        var data = $this.data('changeWatcher');
        if (!data) {
            var cache = $this.val();

            function checkAndTrigger() {
                var val = $this.val();
                if (cache == val) {
                    return false;
                }
                cache = val;
                $this.trigger('changeWatcher.change', val);
                return true;
            }

            $this.bind('change keyup', checkAndTrigger);


            data = {
                bind: function (handler) {
                    $this.bind('changeWatcher.change', handler);
                },
                unbind: function (handler) {
                    $this.unbind('changeWatcher.change', handler);
                },
                reset: function () {
                    cache = $this.val();
                }
            };

            if ($this.attr('autocomplete') == 'on') {
                var checkInterval = setInterval(function () {
                    checkAndTrigger();
                }, 50);
                function acHandler() {
                    clearInterval(checkInterval);
                    data.unbind(acHandler);
                };
                data.bind(acHandler);
            }

            $this.data('changeWatcher', data);
        }
        return data;
    }

    $.fn.changeWatcher = function () {
        ///	<summary>
        /// Initializes user input watcher that can notify observers as soon as user changes input.
        ///	</summary>
        ///	<returns type="ChangeWatcher" />

        var compositeWatcherItems = [];
        var compositeWatcher = {
            bind: function (handler) {
                $.each(compositeWatcherItems, function (ind, item) {
                    item.bind(handler);
                });
            },
            reset: function () {
                $.each(compositeWatcherItems, function (ind, item) {
                    item.reset();
                });
            }
        };

        for (var i = 0; i < this.length; i++) {
            compositeWatcherItems.push(getWatcher(this[i]));
        }

        return compositeWatcher;
    };

})(jQuery);

var notificationArea = (function () {

    var currentItems = [];

    function createItem() {
        var $item = $('<div class="notification-message"></div>');
        $item.css({
            bottom: currentItems.length * 100 + 50
        });
        $('body').append($item);
        var res = {
            setText: function (val) {
                $item.text(val);
            },
            remove: function () {
                $item.animate({ 'opacity': 0 }, {
                    duration: 500,
                    complete: function () {
                        $item.remove();
                        var newItems = [];
                        $.each(currentItems, function (index, item) {
                            if (item != res) newItems.push(item);
                        });
                        currentItems = newItems;
                    }
                });
            },
            addClass: function (className) {
                $item.addClass(className);
            }
        };
        return res;
    }

    return {
        showError: function (message) {
            if (!message)
                return;
            var item = createItem();
            item.setText(message);
            item.addClass('error');
            setTimeout(function () {
                item.remove();
            }, 5000);
            currentItems.push(item);
        },
        showInfo: function (message) {
            var item = createItem();
            item.setText(message);
            item.addClass('info');
            setTimeout(function () {
                item.remove();
            }, 5000);
            currentItems.push(item);
        }

    };

})();

function Utils() {
    var self = this;

    function theSame(a, b) {
        if (a == null && b == null) return true;
        if (a == null && b != null) return false;
        if (b == null && a != null) return false;
        if (typeof (a) != typeof (b)) return false;
        var type = typeof (a);
        if (type == 'string') return a == b;
        if (a == b) return true;

        if ($.isArray(a) && $.isArray(b)) {
            if (a.length != b.length) return false;
            for (var i = 0; i < a.length; i++) {
                if (!theSame(a[i], b[i])) return false;
            }
        }

        for (var keyA in a) {
            if (!theSame(a[keyA], b[keyA])) return false;
        }
        for (var keyB in b) {
            if (!theSame(a[keyB], b[keyB])) return false;
        }
        return true;
    }

    function async(handler) {
        setTimeout(handler, 1);
    }

    function isAlpha(val) {
        if (val >= 'A' && val <= 'Z') return true;
        if (val >= 'a' && val <= 'z') return true;
        return false;
    }
    
    function isDigit(val) {
        if (val >= '0' && val <= '9') return true;
        return false;
    }
    
    function isNullOrWhiteSpace(val) {
        if (val === undefined) {
            return true;
        }
        if (val === null) {
            return true;
        }
        if (val === '') {
            return true;
        }
        for (var i = 0; i < val.length; i++) {
            if (val.charAt(i) != ' ') return false;
        }
        return true;
    }
    
    function getHashCode(str) {
        function toHiLow(val) {
            return {
                hi: val >> 16,
                low: val & 0xffff
            };
        }

        function lshift5(val) {
            var mask = 0x07ff;
            return {
                hi: ((val.hi & mask) << 5) | ((val.low & 0xf800) >> 11),
                low: (val.low & mask) << 5
            };
        }

        function rshift27(val) {
            return {
                hi: 0,
                low: val.hi >> 11
            };
        }

        function rshift1(val) {
            return {
                hi: val.hi >> 1,
                low: ((val.hi & 1) << 15) | (val.low >> 1)
            };
        }

        function add(first, second) {
            var low = first.low + second.low;
            var hi = first.hi + second.hi;
            var carry = low >> 16;
            hi += carry;
            return {
                hi: hi & 0xffff,
                low: low & 0xffff
            };
        }

        function mul(first, second) {
            var val = toHiLow(0);
            for (var i = 0; i < 32; i++) {
                if ((second.low & 1) > 0) {
                    val = add(val, first);
                }
                second = rshift1(second);
                first = add(first, first);
            }
            return val;
        }

        function xor(first, second) {
            return {
                hi: first.hi ^ second.hi,
                low: first.low ^ second.low
            };
        }

        function transform(initial, index) {
            var ch1 = index < str.length ? str.charCodeAt(index) : 0;
            var ch2 = (index + 1) < str.length ? str.charCodeAt(index + 1) : 0;
            var ch = {
                hi: ch2,
                low: ch1
            };
            var x1 = lshift5(initial);
            var x2 = add(x1, initial);
            var x3 = rshift27(initial);
            var x4 = add(x2, x3);
            var val = xor(x4, ch);
            return val;
        }

        var num1 = toHiLow(352654597);
        var num2 = num1;
        var numPtr = 0;
        var length = str.length;

        while (length > 2) {
            num1 = transform(num1, numPtr);
            num2 = transform(num2, numPtr + 2);
            numPtr += 4;
            length -= 4;
        }
        if (length > 0)
            num1 = transform(num1, numPtr);
        var res = add(num1, mul(num2, toHiLow(1566083941)));
        var hix = res.hi.toString(16);
        var lowx = res.low.toString(16);
        while (hix.length < 4) hix = '0' + hix;
        while (lowx.length < 4) lowx = '0' + lowx;
        return hix + lowx;
    }
    
    function parseInt(val) {
        //ecma's parseInt parses '123abc' as 123 and 01234 as hex string.
        val = val.trim();
        if (val.length == 0) return NaN;
        var res = 0;
        for (var i = 0; i < val.length; i++) {
            var ch = val.charAt(i);
            if (ch < '0' || ch > '9') return NaN;
            res = res * 10 + (ch - '0');
        }
        return res;
    }

    var generateId = (function() {
        var seed = 0;

        return function() {
            seed++;
            return seed;
        };
    })();
    
    self.theSame = theSame;
    self.async = async;
    self.isAlpha = isAlpha;
    self.isDigit = isDigit;
    self.isNullOrWhiteSpace = isNullOrWhiteSpace;
    self.getHashCode = getHashCode;
    self.parseInt = parseInt;
    self.generateId = generateId;
}

var utils = new Utils();

$.fn.progressZone = function () {

    function init($this) {

        var opened = 0;

        var $progress;

        return {
            enter: function () {
                if (opened == 0) {
                    $progress = $('<div class="loading" />');
                    $this.append($progress);

                }
                opened++;
            },
            set: function (val) {
                if (val < 0) val = 0;
                while (opened < val) {
                    this.enter();
                }
                while (val < opened) {
                    this.leave();
                }
            },
            leave: function () {
                if (opened > 0) {
                    opened--;
                }
                if (opened == 0 && $progress) {
                    $progress.remove();
                    $progress = null;
                }
            }
        };
    }

    var items = [];

    this.each(function () {
        var $item = $(this);

        var data = $item.data('progressZone');
        if (!data) {
            data = init($item);
            $item.data('progressZone', data);
        }
        items.push(data);
    });

    return {
        enter: function () {
            for (var i = 0; i < items.length; i++) {
                items[i].enter();
            }
        },
        set: function (val) {
            for (var i = 0; i < items.length; i++) {
                items[i].set(val);
            }
        },
        leave: function () {
            for (var i = 0; i < items.length; i++) {
                items[i].leave();
            }
        }
    };

};

jQuery.fn.filterNode = function (name) {
    return this.filter(function () {
        return this.nodeName === name;
    });
};

(function () {

    var chain = $.ajaxSettings.xhr;

    var headers = ["Cache-Control", "Content-Language", "Content-Type", "Date", "ETag", "Expires", "Last-Modified", "Pragma",
                    "x-ms-lease-id", "x-ms-request-id", "x-ms-date", "x-ms-version"];

    $.ajaxSetup({
        xhr: function () {
            var xhr = chain();
            var getAllResponseHeaders = xhr.getAllResponseHeaders;

            xhr.getAllResponseHeaders = function () {
                var allHeaders = getAllResponseHeaders.call(xhr);
                if (allHeaders) {
                    return allHeaders;
                }
                allHeaders = "";
                $(headers).each(function (i, headerName) {
                    var val = xhr.getResponseHeader(headerName);
                    if (val) {
                        allHeaders += headerName + ": " + val + "\n";
                    }
                });
                return allHeaders;
            };
            return xhr;
        }
    });

})();

$.fn.mruList = (function () {

    function init($this) {

        var dispatcher = $({});

        var $container = $('<div class="mruList" />');
        var $list = $('<ul />');
        $container.append($list);

        $this.after($container);

        var opened = false;
        $container.css({
            opacity: 0
        });

        function open() {
            if (!opened) {
                opened = true;
                $container.stop();
                $container.addClass('open');
                $container.animate({
                    opacity: 1
                }, {
                    duration: 150,
                });
            }
        }

        function close() {
            if (opened) {
                opened = false;
                $container.stop();
                $container.animate({
                    opacity: 0
                }, {
                    duration: 150,
                    complete: function () {
                        $container.removeClass('open');
                    }
                });
            }
        }

        $this.bind('focus', function () {
            open();
        });

        $this.bind('blur', function () {
            close();
        });

        function addOnSelect(handler) {
            dispatcher.bind('select', handler);
        }

        function createLink(item) {
            var $link = $('<a href="javascript:void(0)" />');
            $link.text(item);
            $link.bind('click', function () {
                dispatcher.trigger('select', item);
            });
            return $link;
        }

        function setItems(array) {
            $list.empty();

            array = array || [];
            for (var i = 0; i < array.length; i++) {
                var item = array[i];
                if (item) {
                    var $item = $('<li />');
                    var $link = createLink(item);
                    $item.append($link);
                    $list.append($item);
                }
            }
        }

        $this.bind('keypress', function (e) {
            if (e.which == 13) {
                close();
            }
        });

        return {
            open: open,
            close: close,
            setItems: setItems,
            addOnSelect: addOnSelect
        };
    }

    var data = this.data('mruList');
    if (!data) {
        data = init(this);
        this.data('mruList', data);
    }

    return data;
});

function Wildcard(pattern) {

    function transform(str) {
        var res = '';
        for (var i = 0; i < str.length; i++) {
            var ch = str.charAt(i);
            switch (ch) {
                case '.':
                case '\\':
                case '+':
                case '-':
                case '^':
                case '$':
                case '[':
                case ']':
                case '(':
                case ')':
                    res += '\\' + ch;
                    break;
                case '*':
                    res += '.*';
                case '?':
                    res += '.';
                default:
                    res += ch;
            }
        }
        return res;
    }

    pattern = transform(pattern);

    //pattern = '^' + pattern + '$';

    var regex = new RegExp(pattern, 'i');

    function match(str) {
        return regex.test(str);
    }

    this.match = match;
}